package ac.technion.StreamMania;

import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;

import ac.technion.StreamMania.R;
import ac.technion.StreamMania.application.uielements.iface.IParticipantsUIElement;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.graphics.PixelFormat;
import android.media.MediaPlayer;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.SlidingDrawer;
import android.widget.SlidingDrawer.OnDrawerCloseListener;
import android.widget.SlidingDrawer.OnDrawerOpenListener;
import android.widget.TextView;
import android.widget.Toast;

/**
 * This class is the main class of the StreamMania application. 
 * It implements the general workflow of the system, starting from the configuration screen,
 * all the way to the functionality of the sender and the receiver.
 * 
 * @author Arnony
 *
 */
public class StreamMania extends Activity
{
	private StreamingHall m_streamingHall = null;
	
	private Handler m_uiHandler = null;
	
	/*SHANI*/
	SurfaceView surfaceView;
	SurfaceHolder surfaceHolder;
	
	/** Called when the activity is first created. */
	@Override
	public void onCreate(Bundle savedInstanceState)
	{
		super.onCreate(savedInstanceState);
		
		m_uiHandler = new Handler();
		
		List<String> ipAddresses = findOwnIPAddresses();	//User's IP saved in ipAddresses
		triggerFinishIfEmpty(ipAddresses);					//Terminate if user's IP is not available
		
		View configurationView = getLayoutInflater().inflate(R.layout.config, null);	
		ConfigurationScreenManager configMgr = new ConfigurationScreenManager(configurationView, ipAddresses, getApplicationContext());
		configMgr.init();	//Initializes the configuration screen
		
		setContentView(configurationView);	//Switching to the configuration screen.
		
		initSenderButton(configurationView, configMgr);	
		initReceiverButton(configurationView, configMgr);	
	}
	
	/**
	 * The function creates an instance of the StreamingHall class and makes the necessary initializations.
	 * @param participantsUIElement - Displays the members participating in the current session.
	 * @param ownName - The current's user name.
	 * @param broadcastAddress - The address to broadcast.
	 * @param isSender - True iff the current user is a sender.
	 */
	private void startStreamingHall(IParticipantsUIElement participantsUIElement, String ownName, String broadcastAddress, boolean isSender)
	{
		m_streamingHall  = new StreamingHall(ownName, broadcastAddress, audioStreamer, isSender);
				
		m_streamingHall.init(participantsUIElement);
		try
		{
			Thread.sleep(1500);
		}
		catch (InterruptedException e)	{}
		if(isSender && m_streamingHall.doesSenderExist())
		{
			m_streamingHall.quit();
			toastAndExit("There is already a sender.  Returning to main menu.");
		}

		
	}
	
	/**
	 * The function displays a message to the user an terminates the application.
	 * @param textToToast - Message to display.
	 */
	private void toastAndExit(String textToToast) {
		Context context = getApplicationContext();
		CharSequence text = textToToast;
		int duration = Toast.LENGTH_SHORT;
		Toast toast = Toast.makeText(context, text, duration);
		toast.setGravity(Gravity.CENTER, 0, 0);
		toast.show();
		if(!m_streamingHall.getIsSender()){
			audioStreamer.getMediaPlayer().stop();
			audioStreamer.getMediaPlayer().release();
		}
		finish();
	}

	/**
	 * The function displays a message to the user.
	 * @param textToToast - Message to display.
	 */
	private void toastWithoutExit(String textToToast) {
		Context context = StreamMania.this.getBaseContext();
		CharSequence text = textToToast;
		int duration = Toast.LENGTH_SHORT;
		Toast toast = Toast.makeText(context, text, duration);
		toast.setGravity(Gravity.CENTER, 0, 0);
		toast.show();
	}

	/**
	 * The function terminates the application and displays a proper message is no IP address is detected.
	 */
	private void triggerFinishIfEmpty(List<String> ipAddresses)
	{
		if (ipAddresses.isEmpty())
		{
			AlertDialog errMsg = new AlertDialog.Builder(this).create();
			errMsg.setTitle("No network found");
			errMsg.setMessage("Couldn't find available network connections");
			errMsg.setButton("Close", new DialogInterface.OnClickListener()
				{
					public void onClick(DialogInterface paramDialogInterface, int paramInt)
					{
						finish();
					}
				});
			errMsg.show();
		}
	}

	/**
	 * The function defines the members drawer images (left\right arrows)  
	 */
	private void defineDrawerIconFlippingListeners(SlidingDrawer membersDrawer, final ImageView handle)
	{
		membersDrawer.setOnDrawerOpenListener(new OnDrawerOpenListener()
			{
				public void onDrawerOpened()
				{
					handle.setImageResource(R.drawable.right_arrow);
				}
			});
		
		membersDrawer.setOnDrawerCloseListener(new OnDrawerCloseListener()
			{
				public void onDrawerClosed()
				{
					handle.setImageResource(R.drawable.left_arrow);
				}
			});
	}

	/**
	 * The function returns a list of strings representing the user's IP address 
	 */
	private List<String> findOwnIPAddresses()
	{
		List<String> ipAddresses = new ArrayList<String>();
		
		try
		{
			for (Enumeration<NetworkInterface> en = NetworkInterface.getNetworkInterfaces(); en.hasMoreElements();)
			{
				NetworkInterface intf = en.nextElement();
				for (Enumeration<InetAddress> enumIpAddr = intf.getInetAddresses(); enumIpAddr.hasMoreElements();)
				{
					InetAddress inetAddress = enumIpAddr.nextElement();
					if (inetAddress.isLoopbackAddress())
						continue;
					
					ipAddresses.add(inetAddress.getHostAddress().toString());
				}
			}
		}
		catch (Exception ex)
		{
			ex.printStackTrace();
			Toast.makeText(getApplicationContext(), ex.getMessage(), Toast.LENGTH_LONG);
		}
		
		return ipAddresses;
	}
	
/*****************************************************************************/
/*************************** SENDER BEHAVIOR  ********************************/
/*****************************************************************************/

	private Button streamButton;
	
	private Button fileButton;

	private ImageButton playButton;
	
	private Button stopButton;

	private TextView textStreamed;
	
	private ProgressBar progressBar;
	
	private boolean isPaused;

	private boolean isStopped;
		
	private FileInputStream streamedFile;
	
	private  View senderView;
	
	private String mediaFileName;
	private long mediaLengthInKb;
	private long mediaLengthInSeconds;
	
	private final Handler handler = new Handler();
	private boolean sendingFinished = false;
	
	private int count = 0;
		
	private final int BUFFER_SIZE = 16384;

	/**
	 * The function initializes the button pressed by the user asking to be a sender.
	 * @param configurationView - The configuration screen.
	 * @param configMgr - The configuration screen's manager.
	 */
	private void initSenderButton(View configurationView, final ConfigurationScreenManager configMgr) {
		Button sendButton = (Button)configurationView.findViewById(R.id.sender);
		sendButton.setOnClickListener(new OnClickListener()
		{
			public void onClick(View v)
			{
				View senderView1 = initSenderControls();
				setContentView(senderView1);	
				
				senderView1.setKeepScreenOn(true);

				/* participantsUIElement holds the list of participants */
				ParticipantsUIElement participantsUIElement = new ParticipantsUIElement(m_uiHandler, (LinearLayout)senderView1.findViewById(R.id.membersList), configMgr.getName(), getApplicationContext(), true);
				startStreamingHall(participantsUIElement, configMgr.getName(), configMgr.getBroadcastAddress(), true);
			}
		});
	}
	
	/**
	 * The function initializes the different widgets of the sender screen.
	 * @return the view object of the sender screen.
	 */
	private View initSenderControls() { 
		
		senderView = getLayoutInflater().inflate(R.layout.sender_layout, null);
		

		textStreamed = (TextView) senderView.findViewById(R.id.text_kb_streamed);		
		
		SlidingDrawer membersDrawer = (SlidingDrawer)senderView.findViewById(R.id.slide);
		final ImageView handle = (ImageView)senderView.findViewById(R.id.handle);
		defineDrawerIconFlippingListeners(membersDrawer, handle);	//Defines left\right arrows
		
		
		streamButton = (Button) senderView.findViewById(R.id.button_stream);
		streamButton.setOnClickListener(new View.OnClickListener() {
			public void onClick(View view) {
				Runnable r = new Runnable() { 
					public void run() {
						
						if(!checkForListeners(true)){
							return;
						}
						
						textStreamed = (TextView) senderView.findViewById(R.id.text_kb_streamed);		
						playButton = (ImageButton) senderView.findViewById(R.id.button_play);
						stopButton = (Button) senderView.findViewById(R.id.stop_streaming_button);
						fileButton = (Button) senderView.findViewById(R.id.choose_file);
						selectFileToStream();
						//sendNewStreamNotice();
						startSendingAudio();						
			        }   
					
			    };   
			     new Thread(r).start();
			}});
		
		fileButton = (Button) senderView.findViewById(R.id.choose_file);
		fileButton.setOnClickListener(new View.OnClickListener() {
			public void onClick(View view) {
				Intent intent = new Intent();
				intent.setClass(StreamMania.this,AndroidFileBrowser.class);
		    	intent.setAction(android.content.Intent.ACTION_VIEW);//tells us that the action we should do with the intent is to view the data given
		    	startActivityForResult(intent,0);
			    }   
			});

		playButton = (ImageButton) senderView.findViewById(R.id.button_play);
		playButton.setEnabled(false);
		playButton.setOnClickListener(new View.OnClickListener() {
			public void onClick(View view) {
				if (!isPaused) {
					Runnable updater = new Runnable() {
						public void run() {
							playButton.setImageResource(R.drawable.button_play);
							textStreamed.setText((CharSequence) ("Streaming paused..."));
							isPaused = true;
							m_streamingHall.pauseReceivers();
							Log.e("PAUSE", "Sender pressed Pause");
						}
					};
					handler.post(updater);
				} else {
			    	Runnable updater = new Runnable() {
				        public void run() {
							playButton.setImageResource(R.drawable.button_pause);
							textStreamed.setText((CharSequence) ("Streaming continued..."));
							Log.e("PAUSE", "Sender pressed play");
							isPaused = false;
							m_streamingHall.continueReceivers();
				        }
			    	};
			    	handler.post(updater);
				}
			}});

		stopButton = (Button) senderView.findViewById(R.id.stop_streaming_button);
		stopButton.setOnClickListener(new View.OnClickListener() {
			public void onClick(View view) {
				isStopped = true;
				fileButton = (Button) senderView.findViewById(R.id.choose_file);
				m_streamingHall.stopReceivers();
				if(sendingFinished){
					stopStreaming();
					return;
				}
			}

		});
		
		
		Button backToMainButton = (Button) senderView.findViewById(R.id.BackButton);
		backToMainButton.setOnClickListener(new View.OnClickListener() {
			public void onClick(View view) {
				m_streamingHall.stopReceivers();
				try {
					Thread.sleep(1500);
				} 	//Make sure the messages arrive to receivers.
				catch (InterruptedException e) {	e.printStackTrace();	}
				m_streamingHall.quit();
				finish();
			}
		});

	
		return senderView;
	}
	
	/** 
	 * This function enables the user to select a file to stream from his file system.
	 * The input stream for the file is placed in the variable "streamedFile".
	 */
	private void selectFileToStream() {
    	try {
        	File f = new File(fileName);
			streamedFile = new FileInputStream(f);
			isPaused = false;
			isStopped = false;
			sendingFinished = false;
	    	mediaFileName = f.getName();
	    	mediaLengthInKb = f.length()/1024;
	    	
	    	try{
		    	MediaPlayer temp = new MediaPlayer();
		    	FileInputStream stream = new FileInputStream(f);
	        	FileDescriptor fd = stream.getFD();
		    	temp.setDataSource(fd);
		    	temp.prepare();
		    	mediaLengthInSeconds = temp.getDuration()/1000;
		    	temp.release();
	    	}catch (Exception a){
	    		Runnable updater = new Runnable() {
	    			public void run() {
	    				toastAndExit("oooppsss");
	    			}
	    		};
	    		handler.post(updater);
	    	}
	    	
		} catch (FileNotFoundException e) {
			e.printStackTrace();
			toastAndExit("Problem with file selection. Exiting!");
		}
			

	}
	
	private String fileName=null;
	
	/**
	 * The function is called after the sender has selected a media file to stream.
	 */
	protected void onActivityResult(int requestCode, int resultCode, Intent data)
	{
		if(data == null){
			toastWithoutExit("No file chosen");
			return;
		}
		fileName = data.getStringExtra("ac.technion.StreamMania.fileName");
		textStreamed.setText((CharSequence) ("Media file selected:\n" + fileName));
		streamButton.setEnabled(true);
	}	
	
	/** 
	 * The function sends a notice to all receivers that a new stream is about to begin, along with the
	 * details of the streamed file.
	 */
	private void sendNewStreamNotice() {
		m_streamingHall.sendNewStreamNotice(mediaFileName, mediaLengthInKb, mediaLengthInSeconds);
	}
	
	/**
	 * The function handles the streaming of media to the receivers.
	 * The selected media file is divided into blocks and sent to receivers.
	 * When finished sending the file, all relevant text fields are updated.
	 */
	private void startSendingAudio() {
   			
        byte buf[] = new byte[BUFFER_SIZE];
        int numread = 0;
        
		prepareForStream();
		sendNewStreamNotice();
		
		while (true)
		{     	
        	try {
				numread = streamedFile.read(buf);
			} catch (IOException e1) {
				e1.printStackTrace();
				toastAndExit("Problem reading file. Exiting...");
			}
            if (numread <= 0)   
                break;   
            
            /* Send buffer to receivers */
            m_streamingHall.sendStreamingData(buf);
          
            Log.e("COUNT", "Sending package #" + (count) + " of size " + numread);
            count++;

			try {
				Thread.sleep(250);
			} catch (InterruptedException e) {
				e.printStackTrace();
				toastAndExit("Oops... Technical problems. Exiting!");
			}
			
			if(!checkForListeners(false) || isStopped){				
				stopStreaming();
				return;
			}
			
        }    
		sendingFinished = true;
		m_streamingHall.updateSenderFinished();
		/* Done sending data to receivers */
		try {
			streamedFile.close();
		} catch (IOException e) {
			e.printStackTrace();
			toastAndExit("Problem closing file. Exiting...");				
		}	
		Log.e("DONE", "Done Streaming");
		Runnable updater = new Runnable() {
			public void run() {
				textStreamed.setText((CharSequence) ("Media Streaming Completed Successfully"));
			}
		};
		handler.post(updater);

	}
	
	/**
	 * The function updates the text fields and enables the play button. 
	 */
	private void prepareForStream() {	
    	Runnable updater = new Runnable() {
	        public void run() {
	    		textStreamed.setText((CharSequence) ("Streaming File: " + fileName));
	    		playButton.setEnabled(true);
	    		stopButton.setEnabled(true);
	    		fileButton.setEnabled(false);
	    		streamButton.setEnabled(false);
	        }
	    };
	    handler.post(updater);
	}

	/**
	 * The function checks if there are listeners.  Returns true if there are listeners, toasts and returns false otherwise. 
	 * @param waitingForReconnect indicates whether we are expecting a member to reconnect soon.
	 */
	public boolean checkForListeners(final boolean waitingForReconnect){
		Runnable updater = new Runnable() {
	        public void run() {
				if(m_streamingHall.getNumListeners()==0){
					if(waitingForReconnect == false){
						toastWithoutExit("There are currently no listeners. Please wait and try again.");
						senderView.setKeepScreenOn(false);
					}
					else {
						for (int i=0; i<10000000; i++);
						checkForListeners(false);
					}
					return;
				}
				else{
					senderView.setKeepScreenOn(true);
				}

	        }
		};
		handler.post(updater);
		return (m_streamingHall.getNumListeners() != 0);
	}
	
	/**
	 * The function orders all receivers to terminate the current streaming session.
	 * All relevant class members are reset, in order to be ready for the next session.
	 */
	private void stopStreaming(){
		fileName = null;
		count = 0;
		isPaused = false;
		isStopped = true;
    	mediaFileName = null;
    	mediaLengthInKb = 0;
    	mediaLengthInSeconds = 0;
    	m_streamingHall.setSendingStopped();
		
		Runnable updater = new Runnable() {
			public void run() {
				try {
					streamedFile.close();
					streamedFile = null;
					playButton.setEnabled(false);
					streamButton.setEnabled(false);
					stopButton.setEnabled(false);
					fileButton.setEnabled(true);
				} catch (IOException e) {
					e.printStackTrace();
					toastAndExit("Problem closing file. Exiting...");				
				}	
				Log.e("STOP", "Stopped Streaming");
		
				textStreamed.setText((CharSequence) ("Media Streaming Stopped"));
			}
		};
		handler.post(updater);
		
	}
	/*****************************************************************************/
	/************************** RECEIVER BEHAVIOR  *******************************/
	/*****************************************************************************/
	
	private StreamingMediaPlayer audioStreamer;
	
	private TextView textStreamedFileName;
	
	private TextView textSenderStatus;
	
	private boolean isPlaying;
	AndroidVideoPlayer androidVideoPlayer = new AndroidVideoPlayer();
	
	/**
	 * The function initializes the button pressed by the user asking to be a receiver.
	 * @param configurationView - The configuration screen.
	 * @param configMgr - The configuration screen's manager.
	 */
	private void initReceiverButton(View configurationView, final ConfigurationScreenManager configMgr) {
		Button receiveButton = (Button)configurationView.findViewById(R.id.receiver);
		receiveButton.setOnClickListener(new OnClickListener()
		{
			public void onClick(View v)
			{
				View receiverView;
				receiverView = initReceiverControls();
				setContentView(receiverView);	

				ParticipantsUIElement participantsUIElement = new ParticipantsUIElement(m_uiHandler, (LinearLayout)receiverView.findViewById(R.id.membersList), configMgr.getName(), getApplicationContext(), false);
				startStreamingHall(participantsUIElement, configMgr.getName(), configMgr.getBroadcastAddress(), false);

			}
		});
	}
	
	/**
	 * The function initializes the different widgets of the receiver screen.
	 * @return the view object of the receiver screen.
	 */
	private View initReceiverControls() {

		View receiverView = getLayoutInflater().inflate(R.layout.receiver_layout, null);
		
		textStreamed = (TextView) receiverView.findViewById(R.id.text_kb_streamed);
		streamButton = (Button) receiverView.findViewById(R.id.button_stream);
		textSenderStatus = (TextView) receiverView.findViewById(R.id.senderStatusText);
		textStreamedFileName = (TextView) receiverView.findViewById(R.id.streamedFileNameText);

		SlidingDrawer membersDrawer = (SlidingDrawer)receiverView.findViewById(R.id.slide);
		final ImageView handle = (ImageView)receiverView.findViewById(R.id.handle);
		defineDrawerIconFlippingListeners(membersDrawer, handle);	//Defines left\right arrows
		
		Button backToMainButton = (Button) receiverView.findViewById(R.id.BackButton);
		backToMainButton.setOnClickListener(new View.OnClickListener() {
			public void onClick(View view) {
				if(audioStreamer.getMediaPlayer()!=null){
					audioStreamer.stopStreaming();
				}				
				m_streamingHall.quit();
				try {
					Thread.sleep(300);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				finish();
			}
		});
		
		playButton = (ImageButton) receiverView.findViewById(R.id.button_play);
		playButton.setEnabled(false);
		playButton.setOnClickListener(new View.OnClickListener() {
			public void onClick(View view) {
				if (audioStreamer.getMediaPlayer().isPlaying()) {
					audioStreamer.getMediaPlayer().pause();
					playButton.setImageResource(R.drawable.button_play);
				} else {
					audioStreamer.getMediaPlayer().start();
					audioStreamer.startPlayProgressUpdater();
					playButton.setImageResource(R.drawable.button_pause);
				}
				isPlaying = !isPlaying;
			}});
		
		progressBar = (ProgressBar) receiverView.findViewById(R.id.progress_bar);
		/*SHANI*/
	    getWindow().setFormat(PixelFormat.UNKNOWN);
	    surfaceView = (SurfaceView)receiverView.findViewById(R.id.surfaceview);
	    if(surfaceView == null)
	    	toastWithoutExit("1111");

	    surfaceHolder = surfaceView.getHolder();

		audioStreamer = new StreamingMediaPlayer(this,textStreamed, playButton, progressBar, textSenderStatus, textStreamedFileName, androidVideoPlayer,surfaceView,surfaceHolder);
		
		
		return receiverView;		
	}

/*********************************Catch Back Button*******************************/
/*********************************************************************************/
	@Override
	public void onBackPressed() {

		if(m_streamingHall!=null){
			if(m_streamingHall.getIsSender()){
				m_streamingHall.stopReceivers();
				try {	Thread.sleep(1500);	} 	//Make sure the messages arrive to receivers.
				catch (InterruptedException e) {	e.printStackTrace();	}
			}
			else if(audioStreamer!=null && audioStreamer.getMediaPlayer()!=null){
				audioStreamer.stopStreaming();
			}
			m_streamingHall.quit();
		}

		
		super.onBackPressed();
    }

	

}